Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove JSX propTypes validation #28328

Merged
merged 2 commits into from
Feb 21, 2024
Merged

Remove JSX propTypes validation #28328

merged 2 commits into from
Feb 21, 2024

Conversation

gaearon
Copy link
Collaborator

@gaearon gaearon commented Feb 14, 2024

This removes the remaining propTypes validation calls, making declaring propTypes a no-op. In other words, React itself will no longer validate the propTypes that you declare on your components.

In general, our recommendation is to use static type checking (e.g. TypeScript). If you'd like to still run propTypes checks, you can do so manually, same as you'd do outside React:

import checkPropTypes from 'prop-types/checkPropTypes';

function Button(props) {
  checkPropTypes(Button.propTypes, props, 'prop', Button.name)
  // ...
}

This could be automated as a Babel plugin if you want to keep these checks implicit. (We will not be providing such a plugin, but someone in community might be interested in building or maintaining one.)

@react-sizebot
Copy link

react-sizebot commented Feb 20, 2024

Comparing: 4ea424e...2db701a

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 176.87 kB 176.87 kB = 55.14 kB 55.14 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 178.97 kB 178.97 kB = 55.77 kB 55.77 kB
facebook-www/ReactDOM-prod.classic.js = 591.94 kB 591.94 kB = 104.47 kB 104.47 kB
facebook-www/ReactDOM-prod.modern.js = 575.23 kB 575.23 kB = 101.48 kB 101.48 kB
oss-experimental/react/umd/react.development.js = 130.90 kB 126.03 kB = 33.71 kB 32.51 kB
oss-stable/react/umd/react.development.js = 127.10 kB 122.22 kB = 32.58 kB 31.40 kB
oss-stable-semver/react/umd/react.development.js = 127.07 kB 122.20 kB = 32.55 kB 31.37 kB
oss-experimental/react/cjs/react.development.js = 107.55 kB 102.87 kB = 28.96 kB 27.78 kB
oss-stable/react/cjs/react.development.js = 103.89 kB 99.20 kB = 27.85 kB 26.70 kB
oss-stable-semver/react/cjs/react.development.js = 103.86 kB 99.18 kB = 27.82 kB 26.67 kB
facebook-www/React-dev.classic.js = 129.26 kB 123.34 kB = 30.80 kB 29.44 kB
oss-experimental/react-art/Wedge.js = 4.94 kB 4.71 kB = 1.81 kB 1.75 kB
oss-stable-semver/react-art/Wedge.js = 4.94 kB 4.71 kB = 1.81 kB 1.75 kB
oss-stable/react-art/Wedge.js = 4.94 kB 4.71 kB = 1.81 kB 1.75 kB
facebook-www/React-dev.modern.js = 127.68 kB 121.76 kB = 30.34 kB 29.05 kB
facebook-react-native/react/cjs/React-dev.js = 114.96 kB 109.04 kB = 27.32 kB 25.95 kB
oss-experimental/react/cjs/react.react-server.development.js = 88.73 kB 84.05 kB = 24.67 kB 23.53 kB
oss-stable/react/cjs/react.react-server.development.js = 81.17 kB 76.49 kB = 22.36 kB 21.22 kB
oss-stable-semver/react/cjs/react.react-server.development.js = 81.15 kB 76.46 kB = 22.33 kB 21.19 kB
facebook-www/ReactServer-dev.modern.js = 97.39 kB 91.47 kB = 23.01 kB 21.76 kB
oss-experimental/react-art/Circle.js = 1.14 kB 1.04 kB = 0.60 kB 0.56 kB
oss-stable-semver/react-art/Circle.js = 1.14 kB 1.04 kB = 0.60 kB 0.56 kB
oss-stable/react-art/Circle.js = 1.14 kB 1.04 kB = 0.60 kB 0.56 kB
oss-experimental/react/cjs/react-jsx-runtime.react-server.development.js = 48.89 kB 44.23 kB = 14.38 kB 13.22 kB
oss-experimental/react/cjs/react-jsx-runtime.development.js = 48.82 kB 44.17 kB = 14.36 kB 13.20 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.development.js = 47.59 kB 42.94 kB = 14.02 kB 12.84 kB
oss-stable-semver/react/cjs/react-jsx-runtime.react-server.development.js = 47.17 kB 42.52 kB = 13.73 kB 12.56 kB
oss-stable/react/cjs/react-jsx-runtime.react-server.development.js = 47.17 kB 42.52 kB = 13.73 kB 12.56 kB
oss-stable-semver/react/cjs/react-jsx-runtime.development.js = 47.11 kB 42.45 kB = 13.71 kB 12.54 kB
oss-stable/react/cjs/react-jsx-runtime.development.js = 47.11 kB 42.45 kB = 13.71 kB 12.54 kB
oss-stable-semver/react/cjs/react-jsx-dev-runtime.development.js = 45.88 kB 41.22 kB = 13.35 kB 12.19 kB
oss-stable/react/cjs/react-jsx-dev-runtime.development.js = 45.88 kB 41.22 kB = 13.35 kB 12.19 kB
oss-experimental/react-art/Rectangle.js = 3.12 kB 2.79 kB = 1.07 kB 1.00 kB
oss-stable-semver/react-art/Rectangle.js = 3.12 kB 2.79 kB = 1.07 kB 1.00 kB
oss-stable/react-art/Rectangle.js = 3.12 kB 2.79 kB = 1.07 kB 1.00 kB
facebook-www/JSXDEVRuntime-dev.classic.js = 53.79 kB 47.97 kB = 13.91 kB 12.63 kB
facebook-www/JSXDEVRuntime-dev.modern.js = 53.79 kB 47.96 kB = 13.91 kB 12.63 kB
facebook-react-native/react/cjs/JSXRuntime-dev.js = 41.45 kB 35.62 kB = 10.91 kB 9.64 kB
facebook-react-native/react/cjs/JSXDEVRuntime-dev.js = 40.04 kB 34.21 kB = 10.52 kB 9.26 kB
test_utils/ReactAllWarnings.js Deleted 67.04 kB 0.00 kB Deleted 16.43 kB 0.00 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react/umd/react.development.js = 130.90 kB 126.03 kB = 33.71 kB 32.51 kB
oss-stable/react/umd/react.development.js = 127.10 kB 122.22 kB = 32.58 kB 31.40 kB
oss-stable-semver/react/umd/react.development.js = 127.07 kB 122.20 kB = 32.55 kB 31.37 kB
oss-experimental/react/cjs/react.development.js = 107.55 kB 102.87 kB = 28.96 kB 27.78 kB
oss-stable/react/cjs/react.development.js = 103.89 kB 99.20 kB = 27.85 kB 26.70 kB
oss-stable-semver/react/cjs/react.development.js = 103.86 kB 99.18 kB = 27.82 kB 26.67 kB
facebook-www/React-dev.classic.js = 129.26 kB 123.34 kB = 30.80 kB 29.44 kB
oss-experimental/react-art/Wedge.js = 4.94 kB 4.71 kB = 1.81 kB 1.75 kB
oss-stable-semver/react-art/Wedge.js = 4.94 kB 4.71 kB = 1.81 kB 1.75 kB
oss-stable/react-art/Wedge.js = 4.94 kB 4.71 kB = 1.81 kB 1.75 kB
facebook-www/React-dev.modern.js = 127.68 kB 121.76 kB = 30.34 kB 29.05 kB
facebook-react-native/react/cjs/React-dev.js = 114.96 kB 109.04 kB = 27.32 kB 25.95 kB
oss-experimental/react/cjs/react.react-server.development.js = 88.73 kB 84.05 kB = 24.67 kB 23.53 kB
oss-stable/react/cjs/react.react-server.development.js = 81.17 kB 76.49 kB = 22.36 kB 21.22 kB
oss-stable-semver/react/cjs/react.react-server.development.js = 81.15 kB 76.46 kB = 22.33 kB 21.19 kB
facebook-www/ReactServer-dev.modern.js = 97.39 kB 91.47 kB = 23.01 kB 21.76 kB
oss-experimental/react-art/Circle.js = 1.14 kB 1.04 kB = 0.60 kB 0.56 kB
oss-stable-semver/react-art/Circle.js = 1.14 kB 1.04 kB = 0.60 kB 0.56 kB
oss-stable/react-art/Circle.js = 1.14 kB 1.04 kB = 0.60 kB 0.56 kB
oss-experimental/react/cjs/react-jsx-runtime.react-server.development.js = 48.89 kB 44.23 kB = 14.38 kB 13.22 kB
oss-experimental/react/cjs/react-jsx-runtime.development.js = 48.82 kB 44.17 kB = 14.36 kB 13.20 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.development.js = 47.59 kB 42.94 kB = 14.02 kB 12.84 kB
oss-stable-semver/react/cjs/react-jsx-runtime.react-server.development.js = 47.17 kB 42.52 kB = 13.73 kB 12.56 kB
oss-stable/react/cjs/react-jsx-runtime.react-server.development.js = 47.17 kB 42.52 kB = 13.73 kB 12.56 kB
oss-stable-semver/react/cjs/react-jsx-runtime.development.js = 47.11 kB 42.45 kB = 13.71 kB 12.54 kB
oss-stable/react/cjs/react-jsx-runtime.development.js = 47.11 kB 42.45 kB = 13.71 kB 12.54 kB
oss-stable-semver/react/cjs/react-jsx-dev-runtime.development.js = 45.88 kB 41.22 kB = 13.35 kB 12.19 kB
oss-stable/react/cjs/react-jsx-dev-runtime.development.js = 45.88 kB 41.22 kB = 13.35 kB 12.19 kB
oss-experimental/react-art/Rectangle.js = 3.12 kB 2.79 kB = 1.07 kB 1.00 kB
oss-stable-semver/react-art/Rectangle.js = 3.12 kB 2.79 kB = 1.07 kB 1.00 kB
oss-stable/react-art/Rectangle.js = 3.12 kB 2.79 kB = 1.07 kB 1.00 kB
facebook-www/JSXDEVRuntime-dev.classic.js = 53.79 kB 47.97 kB = 13.91 kB 12.63 kB
facebook-www/JSXDEVRuntime-dev.modern.js = 53.79 kB 47.96 kB = 13.91 kB 12.63 kB
facebook-react-native/react/cjs/JSXRuntime-dev.js = 41.45 kB 35.62 kB = 10.91 kB 9.64 kB
facebook-react-native/react/cjs/JSXDEVRuntime-dev.js = 40.04 kB 34.21 kB = 10.52 kB 9.26 kB
test_utils/ReactAllWarnings.js Deleted 67.04 kB 0.00 kB Deleted 16.43 kB 0.00 kB

Generated by 🚫 dangerJS against 2db701a

@gaearon gaearon merged commit 353ecd0 into main Feb 21, 2024
34 of 35 checks passed
@gaearon gaearon deleted the rm-proptypes branch February 21, 2024 11:15
github-actions bot pushed a commit that referenced this pull request Feb 21, 2024
This removes the remaining `propTypes` validation calls, making
declaring `propTypes` a no-op. In other words, React itself will no
longer validate the `propTypes` that you declare on your components.

In general, our recommendation is to use static type checking (e.g.
TypeScript). If you'd like to still run propTypes checks, you can do so
manually, same as you'd do outside React:

```js
import checkPropTypes from 'prop-types/checkPropTypes';

function Button(props) {
  checkPropTypes(Button.propTypes, prop, 'prop', Button.name)
  // ...
}
```

This could be automated as a Babel plugin if you want to keep these
checks implicit. (We will not be providing such a plugin, but someone in
community might be interested in building or maintaining one.)

DiffTrain build for [353ecd0](353ecd0)
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
This removes the remaining `propTypes` validation calls, making
declaring `propTypes` a no-op. In other words, React itself will no
longer validate the `propTypes` that you declare on your components.

In general, our recommendation is to use static type checking (e.g.
TypeScript). If you'd like to still run propTypes checks, you can do so
manually, same as you'd do outside React:

```js
import checkPropTypes from 'prop-types/checkPropTypes';

function Button(props) {
  checkPropTypes(Button.propTypes, prop, 'prop', Button.name)
  // ...
}
```

This could be automated as a Babel plugin if you want to keep these
checks implicit. (We will not be providing such a plugin, but someone in
community might be interested in building or maintaining one.)
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
This removes the remaining `propTypes` validation calls, making
declaring `propTypes` a no-op. In other words, React itself will no
longer validate the `propTypes` that you declare on your components.

In general, our recommendation is to use static type checking (e.g.
TypeScript). If you'd like to still run propTypes checks, you can do so
manually, same as you'd do outside React:

```js
import checkPropTypes from 'prop-types/checkPropTypes';

function Button(props) {
  checkPropTypes(Button.propTypes, prop, 'prop', Button.name)
  // ...
}
```

This could be automated as a Babel plugin if you want to keep these
checks implicit. (We will not be providing such a plugin, but someone in
community might be interested in building or maintaining one.)

DiffTrain build for commit 353ecd0.
@ericfortis
Copy link

Hey @gaearon I’d like to know if you have some advice for overriding render() without Babel.

Since there’s no React.Component.prototype.render, I can’t simply:

const oldRender = React.Component.prototype.render
React.Component.prototype.render = function () {
  checkPropTypes(this.constructor.propTypes, this.props, 'prop', this.constructor.name)
  oldRender()
}

…so I can only think of tediously adding a function to every render, as such:

import { checkPropTypes } from 'prop-types'

React.Component.prototype.checkTypes = function () {
  checkPropTypes(this.constructor.propTypes, this.props, 'prop', this.constructor.name)
}
class Foo extends React.Component {
  render() {
    this.checkTypes()
    return null
  }
}
Foo.propTypes = {
  myProp: PropTypes.string
}

I know you suggested using TypeScript, but TypeScript doesn’t support custom validators as PropTypes do. Also, my project uses integer props names (for performance and obfuscation) so props names are dynamic and proxied, which can’t be done with TS either.

Thanks in advance
Eric

@gaearon
Copy link
Collaborator Author

gaearon commented Aug 1, 2024

I can't think of a seamless way to hack it in — your suggestion above with the manual call per render seems like the easiest way to me. If this is intolerable, then a Babel transform or an equivalent solution seems like the next thing to try. I'd expect a Babel plugin injecting this to take ~30 lines of code.

@gaearon
Copy link
Collaborator Author

gaearon commented Aug 1, 2024

I asked Claude to write a little Babel plugin that does this, here's a bit of code.

module.exports = function(babel) {
  const { types: t } = babel;

  function isReactComponent(node) {
    if (t.isIdentifier(node)) {
      return node.name === 'Component' || node.name === 'PureComponent';
    }
    if (t.isMemberExpression(node)) {
      return (
        t.isIdentifier(node.object) &&
        node.object.name === 'React' &&
        t.isIdentifier(node.property) &&
        (node.property.name === 'Component' || node.property.name === 'PureComponent')
      );
    }
    return false;
  }

  return {
    name: "add-prop-types-check",
    visitor: {
      Program: {
        enter(path, state) {
          state.checkPropTypesIdentifier = path.scope.generateUidIdentifier("checkPropTypes");
        },
        exit(path, state) {
          const newImport = t.importDeclaration(
            [t.importDefaultSpecifier(state.checkPropTypesIdentifier)],
            t.stringLiteral("prop-types/checkPropTypes")
          );
          path.node.body.unshift(newImport);
        }
      },
      ClassDeclaration(path, state) {
        if (path.node.superClass && isReactComponent(path.node.superClass)) {
          const renderMethod = path.node.body.body.find(
            node => t.isClassMethod(node) && node.key.name === "render"
          );

          if (renderMethod) {
            const checkPropsCall = t.expressionStatement(
              t.callExpression(
                state.checkPropTypesIdentifier,
                [
                  t.memberExpression(
                    t.memberExpression(t.thisExpression(), t.identifier("constructor")),
                    t.identifier("propTypes")
                  ),
                  t.memberExpression(t.thisExpression(), t.identifier("props")),
                  t.stringLiteral("prop"),
                  t.memberExpression(
                    t.memberExpression(t.thisExpression(), t.identifier("constructor")),
                    t.identifier("name")
                  )
                ]
              )
            );

            renderMethod.body.body.unshift(checkPropsCall);
          }
        }
      }
    }
  };
};

Might need some tweaking but hope this helps. If you don't use Babel, you might want to port this to a different transform engine.

@ericfortis
Copy link

thank you Dan!

@matyasf
Copy link

matyasf commented Sep 19, 2024

Are propType checks removed from functional components only or from class-based ones too? The RC release notes just speaks about removal from functional components: https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-proptypes-and-defaultprops

@ericfortis
Copy link

@matyasf Yes, in 19, React.Component doesn‘t support propTypes.

@rickhanlonii The release notes say "…for functions"

@rickhanlonii
Copy link
Member

the "from functions" is for default props

@mqklin
Copy link

mqklin commented Dec 6, 2024

Has someone already created this babel plugin?

@gaearon
Copy link
Collaborator Author

gaearon commented Dec 6, 2024

Has someone already created this babel plugin?

#28328 (comment)

@mqklin
Copy link

mqklin commented Dec 7, 2024

Has someone already created this babel plugin?

#28328 (comment)

Thank you! Yep, trying to adjust this now. I use not just prop-types, but forbidExtraProps from airbnb-prop-types, so maybe I need to tweak it more.

UPD: also I don't use class components, but function components:

MyComponent.propTypes = forbidExtraProps({
  children: any.isRequired,
});

function MyComponent({
  children,
}) {
}

So yes, I need to tweak it more for sure.

@mqklin
Copy link

mqklin commented Dec 9, 2024

Claude couldn't help me, but ChatGPT made exactly what I needed.
I don't use Class components, only functional.

propTypesPlugin.js

module.exports = function ({ types: t }) {
  return {
    visitor: {
      Program(path) {
        let usesPropTypes = false;

        // Check if `propTypes` or `forbidExtraProps` is used anywhere in the file
        path.traverse({
          AssignmentExpression(assignPath) {
            if (
              t.isMemberExpression(assignPath.node.left) &&
              assignPath.node.left.property.name === "propTypes"
            ) {
              usesPropTypes = true;
            }
          },
        });

        if (usesPropTypes) {
          // Add `_checkPropTypes` import at the top of the file
          const importDeclaration = t.importDeclaration(
            [t.importDefaultSpecifier(t.identifier("_checkPropTypes"))],
            t.stringLiteral("prop-types/checkPropTypes")
          );
          path.unshiftContainer("body", importDeclaration);
        }
      },
      FunctionDeclaration(path) {
        insertPropTypesCheck(path, t);
      },
      VariableDeclarator(path) {
        // Handle arrow functions assigned to variables
        if (t.isArrowFunctionExpression(path.node.init)) {
          insertPropTypesCheck(path, t);
        }
      },
    },
  };

  function insertPropTypesCheck(path, t) {
    const node = path.node;
    let componentName;

    // Determine the component name
    if (t.isFunctionDeclaration(node)) {
      componentName = node.id.name;
    } else if (t.isVariableDeclarator(node)) {
      componentName = node.id.name;
    } else {
      return;
    }

    // Traverse the program body to find the `propTypes` assignment
    const programNode = path.findParent((parentPath) =>
      t.isProgram(parentPath.node)
    );

    if (!programNode || !programNode.node.body) {
      return; // Exit if the program body is not found
    }

    const programBody = programNode.node.body;

    const propTypesAssignment = programBody.find(
      (n) =>
        t.isExpressionStatement(n) &&
        t.isAssignmentExpression(n.expression) &&
        t.isMemberExpression(n.expression.left) &&
        n.expression.left.object.name === componentName &&
        n.expression.left.property.name === "propTypes"
    );

    if (!propTypesAssignment) {
      return; // Skip if no propTypes assignment found
    }

    let propTypesNode = propTypesAssignment.expression.right;

    // Check if propTypes is wrapped in forbidExtraProps
    if (
      t.isCallExpression(propTypesNode) &&
      t.isIdentifier(propTypesNode.callee) &&
      propTypesNode.callee.name === "forbidExtraProps"
    ) {
      // Insert the _checkPropTypes call with the wrapped propTypes
      const checkPropTypesCall = t.expressionStatement(
        t.callExpression(t.identifier("_checkPropTypes"), [
          t.cloneNode(propTypesNode), // Pass the wrapped propTypes (the result of forbidExtraProps)
          t.identifier("arguments[0]"),
          t.stringLiteral("prop"),
          t.stringLiteral(componentName),
        ])
      );

      // Insert `_checkPropTypes` call at the start of the component
      const functionBody =
        t.isFunctionDeclaration(node)
          ? node.body.body
          : node.init.body.body;

      functionBody.unshift(checkPropTypesCall);
    } else {
      // If not wrapped in forbidExtraProps, handle normally
      const checkPropTypesCall = t.expressionStatement(
        t.callExpression(t.identifier("_checkPropTypes"), [
          t.cloneNode(propTypesNode),
          t.identifier("arguments[0]"),
          t.stringLiteral("prop"),
          t.stringLiteral(componentName),
        ])
      );

      const functionBody =
        t.isFunctionDeclaration(node)
          ? node.body.body
          : node.init.body.body;

      functionBody.unshift(checkPropTypesCall);
    }
  }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants